/* Copyright (c) 2006, 2009, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
 
package com.cburch.draw.model;

import java.awt.Color;
import java.awt.Graphics;
import java.util.List;

import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.UItools.GraphicsUtil;
import com.cburch.logisim.util.IntegerFactory;

abstract class Rectangular extends DrawingMember {
    private Bounds bounds; // this includes the stroke's width
    private Location[] handles;
    private int strokeWidth;
    private Color strokeColor;
    private Color fillColor;
    
    public Rectangular(int x, int y, int w, int h) {
        handles = new Location[] {
            Location.create(x, y),
            Location.create(x + w, y),
            Location.create(x + w, y + h),
            Location.create(x, y + h),
        };
        strokeWidth = 1;
        strokeColor = Color.BLACK;
        fillColor = Color.WHITE;
        recomputeBounds();
    }
    
    public Object getValue(Attribute attr) {
        if(attr == DrawingAttribute.STROKE_COLOR) {
            return strokeColor;
        } else if(attr == DrawingAttribute.FILL_COLOR) {
            return fillColor;
        } else if(attr == DrawingAttribute.STROKE_WIDTH) {
            return IntegerFactory.create(strokeWidth);
        } else {
            return null;
        }
    }
    
    public void setAttrValue(Attribute attr, Object value) {
        if(attr == DrawingAttribute.STROKE_COLOR) {
            strokeColor = (Color) value;
        } else if(attr == DrawingAttribute.FILL_COLOR) {
            fillColor = (Color) value;
        } else if(attr == DrawingAttribute.STROKE_WIDTH) {
            strokeWidth = ((Integer) value).intValue();
            recomputeBounds();
        }
    }
    
    public Bounds getBounds() {
        return bounds;
    }
    
    public void translate(int dx, int dy) {
        bounds = bounds.translate(dx, dy);
        Location[] locs = handles;
        for(int i = 0; i < locs.length; i++) {
            locs[i] = locs[i].translate(dx, dy);
        }
    }
    
    public void getHandles(List dest) {
        dest.clear();
        Location[] locs = handles;
        for(int i = 0; i < locs.length; i++) {
            dest.add(locs[i]);
        }
    }
    
    public void getHandles(List dest, int handleIndex, int dx, int dy) {
        getHandles(dest);
        Location old = (Location) dest.get(handleIndex);
        int oldX = old.getX();
        int oldY = old.getY();
        for(int i = 0; i < 4; i++) {
            Location loc = (Location) dest.get(i);
            if(loc == old) {
                dest.set(i, old.translate(dx, dy));
            } else if(loc.getX() == oldX) {
                dest.set(i, loc.translate(dx, 0));
            } else if(loc.getY() == oldY) {
                dest.set(i, loc.translate(0, dy));
            }
        }
    }

    public void moveHandle(int index, int dx, int dy) {
        Location[] locs = handles;
        locs[index] = locs[index].translate(dx, dy);
        int next = index == locs.length - 1 ? 0 : index + 1;
        int prev = index == 0 ? locs.length - 1 : index - 1;
        if(index % 2 == 0) { // next has same y-coordinate, prev x-coordinate
            locs[next] = locs[next].translate(0, dy);
            locs[prev] = locs[prev].translate(dx, 0);
        } else { // next has same x-coordinate, prev same y-coordinate
            locs[next] = locs[next].translate(dx, 0);
            locs[prev] = locs[prev].translate(0, dy);
        }
        recomputeBounds();
    }
    
    private void recomputeBounds() {
        Location[] locs = handles;
        Bounds interior = Bounds.create(locs[0]).add(locs[2]);
        bounds = strokeWidth < 2 ? interior : interior.expand(strokeWidth / 2);
    }

    public void draw(Graphics g, int xOffs, int yOffs) {
        Location[] locs = handles;
        Location p0 = locs[0];
        Location p1 = locs[2];
        int x0 = xOffs + p0.getX();
        int y0 = yOffs + p0.getY();
        int x1 = xOffs + p1.getX();
        int y1 = yOffs + p1.getY();
        if(x1 < x0) { int t = x0; x0 = x1; x1 = t; }
        if(y1 < y0) { int t = y0; y0 = y1; y1 = t; }

        draw(g, x0, y0, x1 - x0, y1 - y0);
    }
    
    public void draw(Graphics g, int xOffs, int yOffs,
            int hanIndex, int handleDx, int handleDy) {
        Location[] locs = handles;
        Location p0 = locs[hanIndex < 2 ? hanIndex + 2 : hanIndex - 2];
        Location p1 = locs[hanIndex];
        int x0 = xOffs + p0.getX();
        int y0 = yOffs + p0.getY();
        int x1 = xOffs + p1.getX() + handleDx;
        int y1 = yOffs + p1.getY() + handleDy;
        if(x1 < x0) { int t = x0; x0 = x1; x1 = t; }
        if(y1 < y0) { int t = y0; y0 = y1; y1 = t; }

        draw(g, x0, y0, x1 - x0, y1 - y0);
    }
    
    protected boolean setForStroke(Graphics g) {
        if(strokeWidth > 0) {
            GraphicsUtil.switchToWidth(g, strokeWidth);
            g.setColor(strokeColor);
            return true;
        } else {
            return false;
        }
    }
    
    protected boolean setForFill(Graphics g) {
        if(fillColor.getAlpha() != 0) {
            g.setColor(fillColor);
            return true;
        } else {
            return false;
        }
    }
    
    protected int getStrokeWidth() {
        return strokeWidth;
    }
    
    public boolean contains(Location loc) {
        Bounds b = bounds;
        return contains(b.getX(), b.getY(), b.getWidth(), b.getHeight(), loc);
    }
    
    protected abstract boolean contains(int x, int y, int w, int h, Location q);
    protected abstract void draw(Graphics g, int x, int y, int w, int h);
}
